package com.cctc.mychat.common.protocol.codec;

import com.cctc.mychat.common.protocol.message.Message;
import com.cctc.mychat.common.protocol.message.MessageMetadata;
import com.cctc.mychat.common.protocol.message.MessageType;
import com.cctc.mychat.common.protocol.message.MessageVersion;
import com.cctc.mychat.common.protocol.message.impl.request.ChatRequestMessage;
import com.cctc.mychat.common.protocol.message.impl.request.HeartBeatRequestMessage;
import com.cctc.mychat.common.protocol.message.impl.request.LoginRequestMessage;
import com.cctc.mychat.common.protocol.message.impl.request.LogoutRequestMessage;
import com.cctc.mychat.common.protocol.message.impl.response.ChatResponseMessage;
import com.cctc.mychat.common.protocol.message.impl.response.HeartBeatResponseMessage;
import com.cctc.mychat.common.protocol.message.impl.response.LoginResponseMessage;
import com.cctc.mychat.common.protocol.message.impl.response.LogoutResponseMessage;
import com.cctc.mychat.common.protocol.serializer.Serializer;
import com.cctc.mychat.common.protocol.serializer.SerializerType;
import com.cctc.mychat.common.protocol.serializer.impl.JdkSerializer;
import com.cctc.mychat.common.protocol.serializer.impl.JsonSerializer;
import io.netty.buffer.ByteBuf;
import org.omg.CORBA.PUBLIC_MEMBER;

import java.util.Base64;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class MessageCodecManager {
    private static final Map<Byte, Serializer> SERIALIZER_MAP = new ConcurrentHashMap<>();
    private static final Map<Byte, MessageMetadata> MESSAGE_METADATA_MAP = new ConcurrentHashMap<>();

    static {
        SERIALIZER_MAP.put(SerializerType.JDK.getValue(), new JdkSerializer());
        SERIALIZER_MAP.put(SerializerType.JSON.getValue(), new JsonSerializer());
    }

    static {
        MESSAGE_METADATA_MAP.put(MessageType.LOGIN_REQUEST_MESSAGE.getValue(), new MessageMetadata(MessageVersion.VERSION, LoginRequestMessage.class));
        MESSAGE_METADATA_MAP.put(MessageType.LOGIN_RESPONSE_MESSAGE.getValue(), new MessageMetadata(MessageVersion.VERSION, LoginResponseMessage.class));
        MESSAGE_METADATA_MAP.put(MessageType.HEARTBEAT_REQUEST_MESSAGE.getValue(), new MessageMetadata(MessageVersion.VERSION, HeartBeatRequestMessage.class));
        MESSAGE_METADATA_MAP.put(MessageType.HEARTBEAT_REQUEST_MESSAGE.getValue(), new MessageMetadata(MessageVersion.VERSION, HeartBeatResponseMessage.class));
        MESSAGE_METADATA_MAP.put(MessageType.CHAT_REQUEST_MESSAGE.getValue(), new MessageMetadata(MessageVersion.VERSION, ChatRequestMessage.class));
        MESSAGE_METADATA_MAP.put(MessageType.CHAT_RESPONSE_MESSAGE.getValue(), new MessageMetadata(MessageVersion.VERSION, ChatResponseMessage.class));
        MESSAGE_METADATA_MAP.put(MessageType.LOGOUT_REQUEST_MESSAGE.getValue(), new MessageMetadata(MessageVersion.VERSION, LogoutRequestMessage.class));
        MESSAGE_METADATA_MAP.put(MessageType.LOGOUT_RESPONSE_MESSAGE.getValue(), new MessageMetadata(MessageVersion.VERSION, LogoutResponseMessage.class));
    }

    public static final Serializer getSerializer(Byte type) {
        return SERIALIZER_MAP.get(type);
    }

    public static final MessageMetadata getMessageMetadata(Byte type) {
        return MESSAGE_METADATA_MAP.get(type);
    }

    public static final void registrySerializer(Serializer serializer) {
        SERIALIZER_MAP.put(serializer.getSerializerType(), serializer);
    }

    public static final void registryMessageMetadata(Byte type, MessageMetadata metadata) {
        MESSAGE_METADATA_MAP.put(type, metadata);
    }

    public static final synchronized void encode(ByteBuf byteBuf, Message message) {
        byteBuf.writeInt(MessageVersion.MAGIC);
        byteBuf.writeByte(message.getVersion());
        byteBuf.writeByte(Serializer.DEFAULT.getSerializerType());
        byteBuf.writeByte(message.getType());
        Serializer serializer = Serializer.DEFAULT;
        byte[] data = serializer.serialize(message);
        data = Base64.getEncoder().encode(data);
        byteBuf.writeInt(data.length);
        byteBuf.writeBytes(data);
    }

    public static final synchronized Message decode(ByteBuf byteBuf) {
        byteBuf.skipBytes(4);
        byte version = byteBuf.readByte();
        byte serializerType = byteBuf.readByte();
        byte messageType = byteBuf.readByte();
        MessageMetadata metadata = MESSAGE_METADATA_MAP.get(messageType);
        if (metadata == null || version != metadata.getVersion()) return null;
        int length = byteBuf.readInt();
        if (length > 0) {
            byte[] data = new byte[length];
            byteBuf.readBytes(data);
            data = Base64.getDecoder().decode(data);
            Serializer serializer = SERIALIZER_MAP.get(serializerType);
            if (serializer != null && metadata.getType() != null) {
                return serializer.deserialize(metadata.getType(), data);
            }
        }
        return null;
    }

}
